package com.hiqtech.complex.entity;

import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.pow;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;

/**
 * Class represents a complex number. This class is immutable. 
 * Every method returns a new object.
 * 
 */
public final class Complex {
	
	/**
	 * Imaginary unit.
	 */
	public static final Complex J = new Complex(0, 1);
	
	/**
	 * Real part of a complex number.
	 */
	private final double re;

	/**
	 * Imaginary part of a complex number.
	 */
	private final double im;

	/**
	 * Constructor is hidden.
	 * 
	 * @param re
	 *            real part to set
	 * @param im
	 *            imaginary part to set
	 */
	private Complex(double re, double im) {
		this.re = re;
		this.im = im;
	}

	/**
	 * Create a new complex number.
	 * 
	 * @param re
	 *            real part
	 * @param im
	 *            imaginary part
	 * @return new complex number
	 */
	public static Complex create(double re, double im) {
		if (re == 0 && im == 1){
			return Complex.J;
		}
		return new Complex(re, im);
	}

	@Override
	public String toString() {
		return re + " + " + im + "i";
	}

	/**
	 * Returns real part of a number.
	 * 
	 * @return real part of a number
	 */
	public double realPart() {
		return re;
	}

	/**
	 * Returns imaginary part of a number.
	 * 
	 * @return imaginary part of a number
	 */
	public double imaginaryPart() {
		return im;
	}

	public double abs() {
		return sqrt((pow(re, 2) + pow(im, 2)));
	}

	public double arg() {
		return atan2(im, re);
	}

	/**
	 * Returns a reverted complex number.
	 * 
	 * @return a reverted complex number
	 */
	public Complex revert() {
		return Complex.create(re, -im);
	}

	/**
	 * Adds a complex number to this number.
	 * 
	 * @param c
	 *            complex number to add
	 * @return a sum of numbers
	 */
	public Complex add(Complex c) {
		return Complex.create(this.re + c.realPart(), this.im
				+ c.imaginaryPart());
	}

	/**
	 * Multiplies a complex number on another one.
	 * 
	 * @param c
	 *            complex number to multiply
	 * @return a multiplication of two numbers
	 */
	public Complex mult(Complex c) {
		double r = re * c.realPart() - im * c.imaginaryPart();
		double i = re * c.imaginaryPart() + im * c.realPart();
		return Complex.create(r, i);
	}
	
	public Complex subtract(Complex c){
		return Complex.create(re - c.realPart(), im - c.imaginaryPart());
	} 
	
	public Complex devide(Complex c) {
		double r = (re * c.realPart() + im * c.imaginaryPart()) / (pow(c.abs(), 2));
		double i = (im * c.realPart() - re * c.imaginaryPart()) / (pow(c.abs(), 2));
		return Complex.create(r, i);
	}

	/**
	 * Multiplies a complex number on real one.
	 * 
	 * @param d
	 *            real number
	 * @return a complex number multiplied by real one
	 */
	public Complex mult(double d) {
		return Complex.create(d * re, d * im);
	}

	/**
	 * Returns true if this number is real, false otherwise.
	 * 
	 * @return true if this number is real, false otherwise
	 */
	public boolean isReal() {
		return im == 0;
	}
	
	public static Complex exp(double arg){
		return new Complex(cos(arg), sin(arg));
	}

}
